package org.example.me.util;

import com.alibaba.excel.util.StringUtils;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;
import java.util.Objects;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * ZIP压缩工具类
 * 提供将文件夹或文件列表压缩为ZIP文件的功能
 */
public class ZipCompressor {

    private static final int BUFFER_SIZE = 1024;

    /**
     * 压缩文件夹为zip文件，默认删除源文件夹
     *
     * @param sourceFolderPath 源文件夹路径
     * @param outputZipFile    输出zip文件路径
     * @throws IOException IO异常
     */
    public static void compressToZip(String sourceFolderPath, String outputZipFile) throws IOException {
        compressToZip(sourceFolderPath, outputZipFile, true);
    }

    /**
     * 压缩文件夹为zip文件
     *
     * @param sourceFolderPath 源文件夹路径
     * @param outputZipFile    输出zip文件路径
     * @param deleteSource     是否删除源文件夹
     * @throws IOException IO异常
     */
    public static void compressToZip(String sourceFolderPath, String outputZipFile, boolean deleteSource) throws IOException {
        try (FileOutputStream fos = new FileOutputStream(outputZipFile);
             ZipOutputStream zos = new ZipOutputStream(fos)) {
            addFolderToZip(zos, sourceFolderPath, outputZipFile, "", deleteSource);
        }
    }
    
    /**
     * 压缩指定的文件列表到zip文件
     *
     * @param files         要压缩的文件列表
     * @param outputZipFile 输出zip文件路径
     * @param deleteSource  是否删除源文件
     * @throws IOException IO异常
     */
    public static void compressFilesToZip(List<File> files, String outputZipFile, boolean deleteSource) throws IOException {
        try (FileOutputStream fos = new FileOutputStream(outputZipFile);
             ZipOutputStream zos = new ZipOutputStream(fos)) {
            
            for (File file : files) {
                // 跳过输出文件本身
                if (isSameFile(file.getAbsolutePath(), outputZipFile)) {
                    continue;
                }
                
                if (file.isDirectory()) {
                    addFolderToZip(zos, file.getAbsolutePath(), outputZipFile, file.getName() + "/", deleteSource);
                } else {
                    addFileToZip(zos, file, "", deleteSource);
                }
            }
        }
    }

    /**
     * 压缩指定的文件路径列表到zip文件
     *
     * @param filePaths     要压缩的文件路径列表
     * @param outputZipFile 输出zip文件路径
     * @param deleteSource  是否删除源文件
     * @throws IOException IO异常
     */
    public static void compressFilePathsToZip(List<String> filePaths, String outputZipFile, boolean deleteSource) throws IOException {
        try (FileOutputStream fos = new FileOutputStream(outputZipFile);
             ZipOutputStream zos = new ZipOutputStream(fos)) {
            
            for (String filePath : filePaths) {
                // 跳过输出文件本身
                if (isSameFile(filePath, outputZipFile)) {
                    continue;
                }
                
                File file = new File(filePath);
                if (file.isDirectory()) {
                    addFolderToZip(zos, file.getAbsolutePath(), outputZipFile, file.getName() + "/", deleteSource);
                } else {
                    addFileToZip(zos, file, "", deleteSource);
                }
            }
        }
    }

    /**
     * 将文件夹添加到ZIP文件中
     * @param zos ZIP输出流
     * @param sourceFolderPath 源文件夹路径
     * @param outputZipFile 输出ZIP文件路径
     * @param base 基础路径
     * @param deleteSource 是否删除源文件
     * @throws IOException IO异常
     */
    private static void addFolderToZip(ZipOutputStream zos, String sourceFolderPath, String outputZipFile, String base, boolean deleteSource) throws IOException {
        File folder = new File(sourceFolderPath);
        //排除自己
        if (StringUtils.isNotBlank(outputZipFile) && folder.getAbsolutePath().equals(new File(outputZipFile).getAbsolutePath())) {
            return;
        }
        File[] files = folder.listFiles();
        if (files != null) {
            for (File file : files) {
                if (file.isDirectory()) {
                    addFolderToZip(zos, file.getAbsolutePath(), outputZipFile, base + file.getName() + "/", deleteSource);
                    if (deleteSource) {
                        deleteEmptyDirectory(file);
                    }
                } else {
                    addFileToZip(zos, file, base, deleteSource);
                }
            }
        }
        // 删除空文件夹
        if (deleteSource) {
            deleteEmptyDirectory(folder);
        }
    }
    
    /**
     * 将文件添加到ZIP文件中
     * @param zos ZIP输出流
     * @param file 要添加的文件
     * @param base 基础路径
     * @param deleteSource 是否删除源文件
     * @throws IOException IO异常
     */
    private static void addFileToZip(ZipOutputStream zos, File file, String base, boolean deleteSource) throws IOException {
        ZipEntry entry = new ZipEntry(base + file.getName());
        zos.putNextEntry(entry);
        try (FileInputStream fis = new FileInputStream(file)) {
            byte[] buffer = new byte[BUFFER_SIZE];
            int length;
            while ((length = fis.read(buffer)) > 0) {
                zos.write(buffer, 0, length);
            }
        }
        zos.closeEntry();
        if (deleteSource) {
            file.delete();
        }
    }
    
    /**
     * 判断两个文件路径是否指向同一个文件
     * @param filePath1 文件路径1
     * @param filePath2 文件路径2
     * @return 是否为同一文件
     */
    private static boolean isSameFile(String filePath1, String filePath2) {
        return Objects.equals(new File(filePath1).getAbsolutePath(), new File(filePath2).getAbsolutePath());
    }
    
    /**
     * 删除空目录
     * @param directory 目录
     */
    private static void deleteEmptyDirectory(File directory) {
        if (directory.isDirectory() && directory.listFiles() != null && Objects.requireNonNull(directory.listFiles()).length == 0) {
            directory.delete();
        }
    }
}